home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Arsenal Files 3
/
The Arsenal Files 3.iso
/
gen_prog
/
appdemo.exe
/
MAINLIB.APP
< prev
next >
Wrap
Text File
|
1994-11-07
|
43KB
|
1,448 lines
// MAIN A++ LIBRARY
// ================
// This file contains the main library of functions with which you can build
// your program. You can modify it to suit your particular needs. If you
// do then it would be a good idea to lable all your changes with a unique
// string in a comment so that if you get an upgrade you can remake those
// changes.
#define FALSE 0
#define TRUE 1
// Device (File) access modes
#define READ 0
#define WRITE 1
// Devices for Input and Output Streams
#define cin 0 // Can be redirected
#define KEYBOARD 0 // Can be redirected
#define cout 1 // Can be redirected
#define VDU 1 // Can be redirected
#define cerr 2 // Always the VDU
#define AUX 3 // COM1 ?
#define PRN 4 // Normally directed to LPT1
#define LPT1 4
// When files are opened they are allocated device numbers starting
// from 5 by DOS.
// These global variables contain the command line parameters
// They are initialised in Start()
int argc; // Number of arguments in the command line
string argv[10]; // The command line parameters
#ifdef FILEIO
void Stream()
{
// This function is to group data together like a "struct" command but faster
int BufferSegment[MAXHANDLES]; // Segment where the buffer is stored
int BufferPosition[MAXHANDLES]; // Next byte to read from or write to
int BufferLength[MAXHANDLES]; // Last byte from which data can be read
long DOSPosition[MAXHANDLES]; // File Position at which DOS is
byte Err[MAXHANDLES]; // Error condition of File
byte ReadWriteType[MAXHANDLES]; // Type of File Access
byte ColumnNumber[MAXHANDLES]; // Column number used for Tab() function
}
#else
void Stream()
{
// This function is to group data together like a "struct" command in C++
byte Err[5]; // Error condition of File
byte ReadWriteType[5]; // Type of File Access
byte ColumnNumber[5]; // Column number used for Tab() function
}
#endif
void Start()
{
// Start() is where the .EXE program starts.
// It initialises the program, calls Main() and then End()
unsigned int PSPAddress; // PSP segment number
// NOTE :- The Data Segment hasn't been allocated yet, so no variables
// in the Data Segment can be accessed yet
// Check that the CPU is a 386 or above by seeing if the IOPL bits can be set
PUSH FLAGS; POP AX; // Copy flags into AX
AX = AX | 3000h; // Set the IOPL bits
PUSH AX; POP FLAGS; // Copy AX back to flags
PUSH FLAGS; POP AX; // Copy flags back into AX
AX = AX & 3000h; // Isolate the IOPL bits
if(!AX) // If the IOPL bits have been reset then the CPU is a 286 or lower
{
DX = "386 or above required\n$"; // Leave the $ on the end of this for DOS
DS = SS; // Set up DS for DOS. Strings are in the Stack Segment
AH = 9; INT 21h; // Use DOS interrupt because the Streams don't work yet
AX = 4C1Fh; INT 21h; // Terminate program with Error Code 31 (General failure)
}
// The program is initially allocated all available memory, so release
// unused memory here. A paragraph is 16 bytes
BX = SS; AX = ES; BX = BX - AX; // Paragraphs used by program & PSP
BX = BX + SP >> 4 + 2; // Paragraphs used by Stack & Strings
BX = BX + S2[0Ch] >> 4 + 1; // Paragraphs used by Data Segment
SI_ProgramSize = BX; // Store program size for if TSR program
// Note ES is still pointing to the PSP
AH = 4Ah; INT 21h; // Resize memory block of this program
// Allocate Address for Data Segment
AX = SS; AX = AX + SP >> 4 + 2; // Segment Address of Data Segment
DS = AX; // Whew! Now have a Data Segment
// Can can now use Non String Variables
#ifdef TSR
// Store size of this program in paragraghs for TSR programs
unsigned int ProgramSize;
ProgramSize = SI_ProgramSize;
TSRActive = FALSE;
#endif
Error = 0; // Global Error Flag
PSPAddress = ES;
End.HasBeenCalledBefore = FALSE;
// Initialise the String Header by copying it from the Stack Segment
for(SI=0; SI<20h; SI++) D1[SI] = S1[SI];
// Clear all the strings
AX = StringHeapStartAddress + 2; // String NULL Address
DI = StringFirstAddress; // Initialise DI
while(DI .<=. StringLastAddress) // For each String
{
S2[DI] = AX; // Make it a NULL string
DI = DI + 2;
}
// Initialise the String Heap
// This looks rather complicated, but is done so that a NULL string can be
// treated in the same manner as any other string
BX = StringHeapStartAddress;
S1[BX++] = '#'; // Beginning of Heap Marker
S1[BX++] = 0; //
S1[BX++] = 0; // String NULL Address. ie The pointer to "" is always to here
S1[BX++] = '#'; //
S1[BX++] = 0; //
S1[BX] = '#'; // End of Heap Marker. Can start writing string data here
StringNextFreeAddress = BX;
CopyString.Segment = SS; // Default segment for strings
RelocateString.HoleAddress = 0; // There are not any holes in the String Heap yet
// Can now use String Variables as well
#ifdef FILEIO
for(SI=5; SI<MAXHANDLES; SI++)
{
Stream.Err[SI] = 6; // Error number for Invalid Handle
Stream.ColumnNumber[SI] = 0;
}
#endif
// Initialise Devices
for(SI=0; SI<5; SI++)
{
Stream.Err[SI] = 0;
Stream.ReadWriteType[SI] = WRITE;
Stream.ColumnNumber[SI] = 0;
}
Stream.ReadWriteType[0] = READ; // Can only read from keyboard
// Put the complete path and file name of this program in argv[0]
PUSH ES; // Save the PSP address for later
ES = E2[2Ch]; // Get the environment segment address from the PSP
for(DI=0; E1[DI] != 0 || E1[DI+1] != 0; DI++) {} // Search for double NULL
DI = DI + 4; // move past double NULL and length byte
CopyString.Segment = ES; // Segment for source string for String Copy
argv[0] = DI; // Copy the String in
CopyString.Segment = SS; // Default segment for strings
POP ES; // ES = PSP address again
// Put the command line into separate strings argv[1] to argv[argc-1]
BX = StringNextFreeAddress;
CL = E1[80h]; CH = 0; CX = CX + 81h; // Final address of command line string
DI = 1; // Temporary argc
SI = 82h; // Start address of Command line string
while(SI<CX)
{
if(DI >= 10) End(128); // Too many arguments
while(E1[SI] == ' ') SI++; // Skip past blanks
argv[DI] == BX; // Set the string address
while(E1[SI] != ' ' && E1[SI] != 13) { S1[BX] = E1[SI]; BX++; SI++; } // Copy string
if(S1[BX-1] == 0) { argv[DI] == ""; DI--; } // if an empty string then point to NULL string
S1[BX] = 0; // Terminate the string
BX++; // Move past the NULL. BX is still the NextFreeAddress
DI++; // Increment argc
SI++; // Move past the separator in the command line
}
argc = DI;
S1[BX] = '#'; // End of heap marker
StringNextFreeAddress = BX;
//for(SI=0; SI<argc; SI++) cout << argv[SI] << " "; cout << "\n";
#ifdef API
// Need to have a video mode of 80 X 25 color
AH = 0Fh; INT 10h; Vdu.Page = BH; // Get video mode in AL and Page in BH
if(AL != 3) { AX = 0003h; INT 10h; } // if not video mode 3 then make it 3
AH = 03h; INT 10h; // Get cursor position
Vdu.Row = DH; Vdu.Column = DL; // Store cursor position
AH = 08h; INT 10h; // Read cursor Attribute in AH
Vdu.Attribute = AH;
Vdu.BackgroundColor = AH >> 4;
Vdu.ForegroundColor = AH & 0Fh;
Vdu.Blink = 0;
Vdu.MinCol = 0;
Vdu.MinRow = 0;
Vdu.MaxCol = 79;
Vdu.MaxRow = 24;
Vdu.CursorOn = 1;
AX = 0002h; INT 33h; // Hide Mouse
Mouse.Active = 0;
Mouse.Visible = 0;
#endif
// Initialise String Structure List
for(BX=0; BX<MAXSTRUCTURES; BX++) TidyStringHeap.StructureSegment[BX] = 0;
TidyStringHeap.StructureSegment[0] = SS;
AX = Main(); // Call Main() ie. Your program
End(AX); // Call End() with the return code from your program
}
void End(AX_Err)
{
// This Function terminates a program and is called from End()
// or from your program. The parameter passed to it is passed on to DOS
byte HasBeenCalledBefore;
int Err;
string Address;
// Prevent infinite loop if there is an error during the End() function
if(HasBeenCalledBefore) goto End;
HasBeenCalledBefore = TRUE;
// These are the most common error messages from APP.CFG
// You can add more of your own or delete some if you wish
Err = AX_Err;
if (AX_Err == 2) cerr << "File not Found\n";
else if(AX_Err == 3) cerr << "Path not Found\n";
else if(AX_Err == 5) cerr << "Access denied\n";
else if(AX_Err == 6) cerr << "Invalid Handle\n";
else if(AX_Err == 8) cerr << "Insufficient Memory\n";
if(Err && (Err < 100 || Err >= 200)) // if no compilation error
{
//BX = SP; // Determine Error Address from the Stack.
//SI = S2[BX]; // Return Address
//SI = SI - 3; // CALL Address is 3 bytes before Return Address
//Address = IntegerToHexString(SI);
//cerr << "\nProgram terminated at Address " << Address << " with Error " << Err << "\n\n";
//SnapShot("ERR.LOG");
cerr << "\nProgram terminated with Error " << Err << "\n\n";
}
#ifdef FILEIO
// Make sure that all files have been closed
for(SI=5; SI<MAXHANDLES; SI++) if(Stream.Err[SI] != 6) Close(SI);
#endif
End:
#ifdef TSR
if(TSRActive)
{
GetChar(cin);
HasBeenCalledBefore = FALSE;
SP = MainSP; return; // Simulate a return from TSRMain()
}
#endif
#ifdef API
if(Mouse.Visible) HideMouse();
// Clear Screen
CL = 0; CH = 0;
DL = 79; DH = 24;
BH = 7;
AX = 0600h; INT 10h; // BIOS scroll up by 0 (Clear Screen)
// Put cursor at top of screen
BH = 0; DX = 0;
AH = 2; INT 10h;
/*
// Move the DOS cursor to the API cursor
DL = Vdu.Column; DH = Vdu.Row; BH = Vdu.Page;
AH = 02h; INT 10h; // Set cursor position
if(Mouse.Visible) HideMouse();
*/
#endif
AL = Err;
AH = 4Ch;
INT 21h;
}
unsigned int AllocateMemoryBlock(BX)
{
// Allocate a block of memory
// BX is the number of bytes requested
// Returns the Segment Address
BX = BX >> 4; // Convert Bytes to 16 byte Paragraphs
BX = BX + 1; // Allow for rounding
AH = 48h; INT 21h; // Call DOS to Allocate memory Block
if(CARRYFLAG) { Error = AX; return 0; } // If Error
return AX;
}
void ReleaseMemoryBlock(AX)
{
// Release a memory block allocated with AllocateMemoryBlock()
preserve ES;
// BX is the Segment Address to be released
ES = AX;
AH = 49h; INT 21h; // Call DOS to DeAllocate memory Block
if(CARRYFLAG) End(AX); // If invalid then Terminate Program
}
byte InputByte(BX_Handle)
{
// Inputs a Number from a File or Device and returns it as a Byte
string TempString;
TryAgain:
TempString = InputLine(BX_Handle);
PUSH BX;
AX = StringToInteger(TempString);
POP BX;
if(BX_Handle == 0 && !StringToInteger.NumberIsValid) goto TryAgain;
return AL;
}
int InputInteger(BX_Handle)
{
// Inputs a Number from a File or Device and returns it as an Integer
string TempString;
TryAgain:
TempString = InputLine(BX_Handle);
PUSH BX;
AX = StringToInteger(TempString);
POP BX;
if(BX_Handle == 0 && !StringToInteger.NumberIsValid) goto TryAgain;
return AX;
}
long InputLongInteger(BX_Handle)
{
// Inputs a Number from a File or Device and returns it as a Long Integer
string TempString;
TryAgain:
TempString = InputLine(BX_Handle);
PUSH BX;
EAX = StringToLongInteger(TempString);
POP BX;
if(BX_Handle == 0 && !StringToLongInteger.NumberIsValid) goto TryAgain;
return EAX;
}
string InputLine(BX_Handle)
{
// Input a line (Terminated with a Carriage Return) from a file or device
int Handle;
string Line;
byte BufferToppedUp;
int BufferSegment;
int BufferPosition;
int BufferLength;
byte OldCursorStatus;
char Character;
preserve BX, DX, SI, DI, ES;
if(Stream.Err[BX]) { Line = ""; return Line; }
if(BX == 0)
{
#ifdef API
// Move the BIOS cursor to the API cursor
PUSH BX;
OldCursorStatus = Vdu.CursorOn;
CursorOn();
POP BX;
#endif
#ifdef TSR
// If Device == 0 then read a string from the Keyboard
cout << "? ";
DI = ScratchPadAddress; // The String is copied into the Scratch Pad
AH = 00h; INT 16h; // Read the first byte from the keyboard
while(AL != 13) // While not a Carriage Return
{
if(AL == 0) // If extended character
{
if(AH == 4Bh) AL = 8; // if Left Arrow replace with Backspace
}
if(AL == 8 && DI .>. ScratchPadAddress) // If Backspace and something entered
{
PUSH AX;
cout << "\b \b"; // Move back, delete, move back
POP AX;
DI--;
}
if(AL > 31 && AL < 127) // If character valid
{
S1[DI] = AL; // Store the Character
Character = AL; cout << Character; // Display the Character
DI++; // Point to the next Address
}
DL = Vdu.Column; DH = Vdu.Row; BH = Vdu.Page;
AH = 02h; INT 10h; // Set cursor position
AH = 00h; INT 16h; // Read the first byte from the keyboard
}
#else
// If Device == 0 then read a string from the Keyboard
cout << "? ";
DI = ScratchPadAddress; // The String is copied into the Scratch Pad
AH = 08h; INT 21h; // Read the first byte from the keyboard
while(AL != 13) // While not a Carriage Return
{
if(AL == 0) // If extended character
{
AH = 08h; INT 21h; // Read extended character
if(AL == 75) AL = 8; // if Left Arrow replace with Backspace
else AL = 0; // else Ignore
}
if(AL == 8 && DI .>. ScratchPadAddress) // If Backspace and something entered
{
PUSH AX;
cout << "\b \b"; // Move back, delete, move back
POP AX;
DI--;
}
if(AL > 31 && AL < 127) // If character valid
{
S1[DI] = AL; // Store the Character
Character = AL; cout << Character; // Display the Character
DI++; // Point to the next Address
}
AH = 8; INT 21h; // Read the next byte from the keyboard
}
#endif
S1[DI] = 0; // Terminate the String
Line = ScratchPadAddress; // Copy String from Scratch pad to proper location
cout << "\n";
#ifdef API
// Restore old cursor state
if(!OldCursorStatus) CursorOff();
#endif
return Line;
}
#ifdef FILEIO
if(BX_Handle >= MAXHANDLES || Stream.Err[BX] == 6) End(6); // If Invalid Handle then terminate program
if(Stream.ReadWriteType[BX_Handle] != READ) End(5); // Cannot Read from this Device / File
// For speed copy Subscripted variables into Non subscripted variables
Handle = BX_Handle;
BufferSegment = Stream.BufferSegment[BX_Handle];
BufferPosition = Stream.BufferPosition[BX_Handle];
BufferLength = Stream.BufferLength[BX_Handle];
BufferToppedUp = 0;
// See if there is a Carriage Return in the existing string
SearchForCR:
ES = BufferSegment;
for(SI=BufferPosition; SI < BufferLength; SI++)
{
if(E1[SI] == 9) E1[SI] = ' '; // Replace TAB with SPACE
if(E1[SI] == 13 || E1[SI] == 26) // CR or EOF
{
if(E1[SI] == 26) Stream.Err[BX] = 204; // Set EOF Flag
// If this is the last character then we don't know if the next character
// is a Line Feed or not, so Top up the Buffer
if(SI+1 == BufferLength && E1[SI] == 13 && BufferToppedUp == 0) break;
DI_OldSI = SI; DL_OldChar = E1[SI];
E1[SI] = 0;
SI++; if(SI < BufferLength && E1[SI] == 10) SI++; // Move past CR, LF
// Copy the string in
CopyString.Segment = ES;
Line = CopyString(BufferPosition);
CopyString.Segment = SS;
BufferPosition = SI;
E1[DI] = DL_OldChar;
// Copy back again
BX_Handle = Handle;
Stream.BufferPosition[BX_Handle] = BufferPosition;
Stream.BufferLength[BX_Handle] = BufferLength;
//PUSH BX;
//LogFile << Line << "\n";
//POP BX;
return Line;
}
}
if(BufferToppedUp) End(201); // If string too long for buffer then Terminate program
// A carriage Return has not been found so Read in some more data
// First copy the remaining data back to the start
BX = 0;
for(SI=BufferPosition; SI<BufferLength; SI++) { E1[BX] = E1[SI]; BX++; }
BufferPosition = 0;
BufferLength = BX;
// Next Read in the Data
DX = BX;
CX = FILEBUFFERSIZE - BX;
BX = Handle;
PUSH DS; PUSH BX;
DS = BufferSegment;
//Dot();
AH = 3Fh; INT 21h; // Read the bytes from the device
POP BX; POP DS;
if(CARRYFLAG)
{
Stream.Err[BX_Handle] = AL; Error = AX;
Line = "";
return Line;
}
if(AX == 0) // If no bytes read then must be end of File
{
Stream.Err[BX] = 204; // Set File Error Flag
Line = "";
return Line;
}
BufferLength = BufferLength + AX;
ECX = 0; CX = AX; Stream.DOSPosition[BX_Handle] = Stream.DOSPosition[BX_Handle] + ECX;
BufferToppedUp = 1;
goto SearchForCR;
return Line; // Prevent Compilation Error
#endif
}
string InputLineWithDefault(string DefaultString)
{
// Input a line (Terminated with a Carriage Return) from the keyboard
// with a default string which can be modified
string Line;
byte BufferToppedUp;
int BufferSegment;
int BufferPosition;
int BufferLength;
byte OldCursorStatus;
char Character;
preserve BX, DX, SI, DI;
#ifdef API
// Move the BIOS cursor to the API cursor
OldCursorStatus = Vdu.CursorOn;
CursorOn();
#endif
#ifdef TSR
// If Device == 0 then read a string from the Keyboard
cout << "? " << DefaultString;
DI = ScratchPadAddress; // The String is copied into the Scratch Pad
SI = 0; while(DefaultString[SI]) S1[DI++] = DefaultString[SI++];
AH = 00h; INT 16h; // Read the first byte from the keyboard
while(AL != 13) // While not a Carriage Return
{
if(AL == 0) // If extended character
{
if(AH == 4Bh) AL = 8; // if Left Arrow replace with Backspace
}
if(AL == 8 && DI .>. ScratchPadAddress) // If Backspace and something entered
{
PUSH AX;
cout << "\b \b"; // Move back, delete, move back
POP AX;
DI--;
}
if(AL > 31 && AL < 127) // If character valid
{
S1[DI] = AL; // Store the Character
Character = AL; cout << Character; // Display the Character
DI++; // Point to the next Address
}
DL = Vdu.Column; DH = Vdu.Row; BH = Vdu.Page;
AH = 02h; INT 10h; // Set the cursor position
AH = 00h; INT 16h; // Read the next byte from the keyboard
}
#else
cout << "? " << DefaultString;
DI = ScratchPadAddress; // The String is copied into the Scratch Pad
// The next line copies the Default String into the scratchpad
SI = 0; while(DefaultString[SI]) S1[DI++] = DefaultString[SI++];
AH = 08h; INT 21h; // Read the first byte from the keyboard
while(AL != 13) // While not a Carriage Return
{
if(AL == 0) // If extended character
{
AH = 08h; INT 21h; // Read extended character
if(AL == 75) AL = 8; // if Left Arrow replace with Backspace
else AL = 0; // else Ignore
}
if(AL == 8 && DI .>. ScratchPadAddress) // If Backspace and something entered
{
PUSH AX;
cout << "\b \b"; // Move back, delete, move back
POP AX;
DI--;
}
if(AL > 31 && AL < 127) // If character valid
{
S1[DI] = AL; // Store the Character
Character = AL; cout << Character; // Display the Character
DI++; // Point to the next Address
}
AH = 8; INT 21h; // Read the next byte from the keyboard
}
#endif
S1[DI] = 0; // Terminate the String
Line = ScratchPadAddress; // Copy String from Scratch pad to proper location
cout << "\n";
#ifdef API
// Restore old cursor state
if(!OldCursorStatus) CursorOff();
#endif
return Line;
}
byte AKeyIsWaiting()
{
// Detects if a key is waiting in the Keyboard buffer
// Returns 0 if no key has been pressed or 1 if a key has been pressed
#ifdef TSR
PUSH ES;
AX = 0040h; ES = AX;
if(E2[001Ah] == E2[001Ch]) AL = 0; // Compare keyboard Head and Tail pointers
else AL = 1;
POP ES;
#else
AH = 0Bh; INT 21h;
AL = AL & 1; // Change 0xFF to 1
#endif
return AL;
}
char GetChar(BX_Handle)
{
// Input a character from a file or device
AL = GetByte(BX_Handle);
return AL;
}
byte GetByte(BX_Handle)
{
// Input a Binary byte from a file or device
preserve BX, DX, DI, ES;
DI_Handle = BX_Handle;
if(BX == 0)
{
// If Device == 0 then read a string from the Keyboard
#ifdef TSR
// TSR's cannot use DOS function 08h, so use BIOS instead
AL = 00h;
while(AL == 0) // While no valid character read
{
DL = Vdu.Column; DH = Vdu.Row; BH = Vdu.Page;
AH = 02h; INT 10h; // Set cursor position
AH = 00h; INT 16h; // Read the next byte from the keyboard
}
#else
// Use DOS function to read key rather than BIOS so that
// input can be redirected
AH = 08h; INT 21h; // Read the first byte from the keyboard
while(AL == 0) // While an extended character
{
AH = 08h; INT 21h; // Read extended character
AH = 08h; INT 21h; // Read the next byte from the keyboard
}
#endif
return AL;
}
#ifdef FILEIO
if(DI_Handle >= MAXHANDLES || Stream.Err[DI_Handle] == 6) End(6); // If Invalid Handle then terminate program
if(Stream.ReadWriteType[DI_Handle] != READ) End(5); // Cannot Read from this Device / File
ES = Stream.BufferSegment[DI_Handle];
//LogFile << "Handle = " << DI_Handle << "\n";
//LogFile << "Segment = " << Stream.BufferSegment[DI_Handle] << "\n";
//LogFile << "Position = " << Stream.BufferPosition[DI_Handle] << "\n";
//LogFile << "Length = " << Stream.BufferLength[DI_Handle] << "\n";
if(Stream.BufferPosition[DI_Handle] >= Stream.BufferLength[DI_Handle])
{
// There are not any more characters in the buffer, so read some more in
DX = 0;
CX = FILEBUFFERSIZE;
BX = DI_Handle;
PUSH DS;
DS = Stream.BufferSegment[DI_Handle];
//Dot();
AH = 3Fh; INT 21h; // Read the bytes from the device
POP DS;
if(CARRYFLAG)
{
Stream.Err[DI_Handle] = AL; Error = AX;
return 0;
}
if(AX == 0) // If no bytes read then must be end of File
{
Stream.Err[DI_Handle] = 204; // Set File Error Flag
return 0;
}
Stream.BufferPosition[DI_Handle] = 0;
Stream.BufferLength[DI_Handle] = AX;
ECX = 0; CX = AX; Stream.DOSPosition[DI_Handle] = Stream.DOSPosition[DI_Handle] + ECX;
}
BX_Handle = DI_Handle; // Now use BX to store the Handle
DI = Stream.BufferPosition[BX_Handle];
Stream.BufferPosition[BX_Handle]++;
//LogFile << "char[0] = " << E1[DI] << "\n";
return E1[DI];
#else
End(5); // Invalid Handle
return 0;
#endif
}
int GetInt(BX_Handle)
{
// Input a binary integer from a file or device
char LowerByte;
LowerByte = GetChar(BX_Handle);
AH = GetChar(BX_Handle);
AL = LowerByte;
return AX;
}
long GetLong(BX_Handle)
{
// Input a binary long integer from a file or device
char LowerByte;
int LowerWord;
LowerByte = GetChar(BX_Handle);
AH = GetChar(BX_Handle);
AL = LowerByte;
LowerWord = AX;
LowerByte = GetChar(BX_Handle);
EAX = 0;
AH = GetChar(BX_Handle);
AL = LowerByte;
EAX = EAX << 16;
AX = LowerWord;
return EAX;
}
void PrintChar(BX_Handle, AL)
{
// Print one character
PutByte(BX_Handle, AL);
}
void PrintByte(BX_Handle, AL)
{
// Print a signed byte as a number
AX = SIGNEXTEND(AL);
PrintInteger(BX_Handle, AX);
}
void PrintUnsignedByte(BX_Handle, AL)
{
// Print an unsigned byte as a number
AH = 0;
PrintUnsignedInteger(BX_Handle, AX);
}
void PrintInteger(BX_Handle, CX)
{
// Print a signed integer as a number
byte NegativeNumber;
string PrtString;
preserve DX, DI;
PUSH BX;
PrtString = IntegerToString(CX);
POP BX;
PrintString(BX, PrtString);
}
void PrintUnsignedInteger(BX_Handle, AX)
{
// Print an unsigned integer as a number
preserve DX, DI;
DI = ScratchPadAddress + 30;
S1[DI] = 0;
CX = 10;
if(AX == 0) { DI--; S1[DI] = '0'; }
while(AX .>. 0) // Unsigned compare
{
DX = 0;
AX = DX:AX ./. CX; // DX = Remainder of Unsigned Divide
DL = DL + 48;
DI--;
S1[DI] = DL;
}
PrintString(BX_Handle, DI);
}
void PrintLongInteger(BX_Handle, EAX_Value)
{
// Print a signed long integer as a number
byte NegativeNumber;
preserve EDX, DI;
DI = ScratchPadAddress + 30;
S1[DI] = 0;
ECX = 10;
if(EAX == 0) { DI--; S1[DI] = '0'; }
NegativeNumber = 0;
if(EAX < 0) { EAX = - EAX; NegativeNumber = 1; }
while(EAX > 0)
{
EDX = 0;
EAX = EDX:EAX / ECX; // EDX = Remainder
DL = DL + 48;
DI--;
S1[DI] = DL;
}
if(NegativeNumber) { DI--; S1[DI] = '-'; }
PrintString(BX_Handle, DI);
}
void PrintString(BX_Handle, AX_StringToPrint)
{
// Print a string
int Handle;
unsigned int Len;
unsigned int BufferPosition;
unsigned int BufferSegment;
preserve ALL, ES;
BP_StringToPrint = AX_StringToPrint;
Handle = BX_Handle;
Len = StringLength(BP_StringToPrint);
if(Len == 0) return;
BX = Handle;
if(BX_Handle < 5)
{
#ifdef API
if(BX_Handle < 3)
{
if(Mouse.Visible)
{
HideMouse();
while(S1[BP]) PutByte(BX, S1[BP++]);
ShowMouse();
}
else while(S1[BP]) PutByte(BX, S1[BP++]);
return;
}
#endif
// If a hardware device then write it out straight away
CX = Len; DX = BP;
PUSH DS; DS = SS; AH = 40h; INT 21h; POP DS; // Write data
Stream.ColumnNumber[BX_Handle] = Stream.ColumnNumber[BX_Handle] + Len;
SI = BP + Len; // Point to end of string
if(S1[SI-1] == 13 || S1[SI-2] == 13) Stream.ColumnNumber[BX_Handle] = 0;
return;
}
#ifdef FILEIO
BX = Handle;
if(Stream.ReadWriteType[BX_Handle] != WRITE) End(5);
BufferPosition = Stream.BufferPosition[BX_Handle];
BufferSegment = Stream.BufferSegment[BX_Handle];
if(BufferPosition + Len .>=. FILEBUFFERSIZE)
{
Flush(Handle);
BufferPosition = 0;
if(Len .>=. FILEBUFFERSIZE) End(201); // If too Long then Terminate Program
}
// Copy into Buffer
ES = BufferSegment;
SI = BP_StringToPrint;
DI = BufferPosition;
while(S1[SI] != 0) { E1[DI] = S1[SI]; SI++; DI++; }
BX_Handle = Handle;
Stream.BufferPosition[BX_Handle] = BufferPosition + Len;
Stream.ColumnNumber[BX_Handle] = Stream.ColumnNumber[BX_Handle] + Len;
if(S1[SI-1] == 13 || S1[SI-2] == 13) Stream.ColumnNumber[BX_Handle] = 0;
#endif
}
void PutByte(BX_Handle, CL_Byte)
{
// Write a byte in binary form
byte Byte;
byte PrintableChar;
preserve DX, DI, ES;
if(BX_Handle < 5)
{
Byte = CL;
#ifdef API
if(BX_Handle < 3)
{
AL = Vdu.Page; AH = 0; AX = AX * 100h + B800h; ES = AX; // Page address
Vdu.Attribute = ( Vdu.ForegroundColor + Vdu.BackgroundColor << 4 ) | Vdu.Blink;
PrintableChar = TRUE;
if(CL == 7 ) { Beep(); PrintableChar = FALSE; } // Bell
if(CL == 8 ) { Vdu.Column--; PrintableChar = FALSE; } // Back Space
if(CL == 10) { Vdu.Row++; PrintableChar = FALSE; } // Line Feed
if(CL == 13) { Vdu.Column = Vdu.MinCol; PrintableChar = FALSE; } // Carriage Return
if(Vdu.Row > Vdu.MaxRow)
{
Vdu.Row--;
/*
// Scroll screen up
for(DI=0; DI<3840; DI=DI+4) E4[DI] = E4[DI+160];
// Clear the bottom line
for(DI=3840; DI<4000; DI=DI+2) { E1[DI] = ' '; E1[DI+1] = Vdu.Attribute; }
*/
CL = Vdu.MinCol;
CH = Vdu.MinRow;
DL = Vdu.MaxCol;
DH = Vdu.MaxRow;
BH = Vdu.Attribute;
if(Mouse.Visible)
{
HideMouse();
AX = 0601h; INT 10h; // BIOS scroll up
ShowMouse();
}
else { AX = 0601h; INT 10h; } // BIOS scroll up
}
if(Vdu.Column < Vdu.MinCol) return;
if(Vdu.Row < Vdu.MinRow) return;
if(Vdu.Column > Vdu.MaxCol) return;
if(Vdu.Row > Vdu.MaxRow) return;
if(PrintableChar)
{
// Calculate the character position on the screen
AL = Vdu.Column; AH = 0;
DI = AX;
AL = Vdu.Row;
DI = (DI + AX * 80) * 2;
// Write the character
if(Mouse.Visible)
{
HideMouse();
E1[DI] = Byte;
E1[DI+1] = Vdu.Attribute;
ShowMouse();
}
else
{
E1[DI] = Byte;
E1[DI+1] = Vdu.Attribute;
}
Vdu.Column++;
}
Stream.ColumnNumber[cout] = Vdu.Column;
Stream.ColumnNumber[cerr] = Vdu.Column;
if(Vdu.CursorOn)
{
// Update BIOS cursor position
DL = Vdu.Column; DH = Vdu.Row; BH = Vdu.Page;
AH = 02h; INT 10h; // Set cursor position
}
return;
}
#endif
// If a hardware device then write it out straight away
CX = 1; AX = ADDRESSOF(Byte); DX = AX;
AH = 40h; INT 21h; // Write data
Stream.ColumnNumber[BX_Handle]++;
if(Byte == 13) Stream.ColumnNumber[BX_Handle] = 0;
return;
}
#ifdef FILEIO
if(Stream.ReadWriteType[BX_Handle] != WRITE) End(5);
if(Stream.BufferPosition[BX_Handle] + 1 >= FILEBUFFERSIZE) { PUSH CX; Flush(BX_Handle); POP CX;}
ES = Stream.BufferSegment[BX_Handle];
DI = Stream.BufferPosition[BX_Handle];
E1[DI] = CL_Byte;
Stream.BufferPosition[BX_Handle]++;
Stream.ColumnNumber[BX_Handle]++;
if(CL_Byte == 13) Stream.ColumnNumber[BX_Handle] = 0;
#endif
}
void PutInt(BX_Handle, CX_Int)
{
// Write an integer in binary form
PutByte(BX_Handle, CL);
PutByte(BX_Handle, CH);
}
void PutLong(BX_Handle, ECX)
{
// Write a long integer in binary form
preserve EDX;
EDX = ECX << 16;
PutByte(BX_Handle, CL);
PutByte(BX_Handle, CH);
PutByte(BX_Handle, DL);
PutByte(BX_Handle, DH);
}
void Tab(BX_Handle, CL)
{
// Move to a column number by filling in with spaces
byte TabPosition;
preserve BX;
TabPosition = CL - 1;
while(Stream.ColumnNumber[BX] < TabPosition) PrintChar(BX, ' ');
}
int DayOfWeek(int Day, int Month, int Year)
{
// Calculates the day of the week given the date
// The year must be the Full year. ie. 1994 and not 94
// Returns 0 = Sunday, 1 = Monday, ... 6 = Saturday
preserve EDX;
// To make Leap years work correctly make each year start from 1st March
if(Month < 3) { Month = Month + 12; Year--; }
// Calculate number of Days in Years as :- Years * 365.25
EBX = 0; BX = Year; // Convert Year from 16 to 32 bit
EAX = EBX_Year * 36525; // Days since year Dot Times 100
EDX = 0; EBX = 100; // Prepare registers for 64 bit divide
DIV EBX; // Unsigned Divide EDX:EAX by EBX (100)
ECX_DayNumber = EAX; // Days since year Dot
// Calculate number of Days in Months as :- (Months + 1) * 30.6
EBX = 0; BX = Month; // Convert Month from 16 to 32 bit
EAX = (EBX_Month + 1) * 306; // 30.6 days per month
EDX = 0; EBX = 10; // Prepare registers for 64 bit divide
DIV EBX; // Unsigned Divide by 10
ECX_DayNumber = ECX_DayNumber + EAX; // Add days in months to days in years
// Add Days
EBX = 0; BX = Day; // Convert Day of Month from 16 to 32 bit
ECX_DayNumber = ECX_DayNumber + EBX; // Add Days
// Every 100 years a leap year is skipped
EAX = 0; AX = Year; // Convert Year from 16 to 32 bit
EDX = 0; EBX = 100; // Prepare registers for 64 bit divide
DIV EBX; // Unsigned Divide Year by 100
ECX_DayNumber = ECX_DayNumber - EAX; // Subtract days skipped
// Every 400 years a leap year is added
EAX = 0; AX = Year; // Convert Year from 16 to 32 bit
EDX = 0; EBX = 400; // Prepare registers for 64 bit divide
DIV EBX; // Unsigned Divide Year by 400
ECX_DayNumber = ECX_DayNumber + EAX; // Add Extra days
// Calculate the remainder of a divide by 7
EAX = ECX_DayNumber - 1; // Subtract a day so that days begin on Sunday
EDX = 0; EBX = 7; // Prepare registers for 64 bit divide
DIV EBX; // Unsigned Divide by 7. EDX contains remainder
return DX; // 16 bit version of EDX has remainder
// The Day number returned in ECX can be used to calculate the number
// of days between two dates.
}
void SnapShot(string SnapShotFileExtension)
{
// This can be used like a flight recorder to determine what state the program
// was in when an error occured, or you can use it to take snap shots of the
// program at various places to find an elusive bug
string CommandLine;
string ThisProgName;
// Get the Current File Name
SI = 0; while(argv[0][SI] != '.') SI++; // Search for '.'
while(argv[0][SI] != '\') SI--; // Search back for '\'
DI = ScratchPadAddress; SI++;
while(argv[0][SI] != '.') S1[DI++] = argv[0][SI++]; // Copy String in
S1[DI] = 0;
ThisProgName = ScratchPadAddress;
// Note you must supply the full path of file SNAPSHOT here
CommandLine = "C:\APP\SNAPSHOT.EXE " + ThisProgName
+ "." + SnapShotFileExtension
+ " " + IntegerToHexString(SS)
+ " " + IntegerToHexString(SP)
+ " " + IntegerToHexString(DS)
+ " " + IntegerToHexString(CS);
//cout << CommandLine << "\n";
RunChildProgram(CommandLine);
}
int Shell(string CommandLine)
{
// Run a DOS command, or make a DOS prompt if "" is enterd for the Command
string COMSPECString;
preserve DI, ES;
ES = Start.PSPAddress;
ES = E2[2Ch]; // Get the environment segment address from the PSP
DI = 0;
while(AX == AX) // Forever
{
// Search for "COMSPEC"
if (E1[DI] == 'C'
&& E1[DI+1] == 'O'
&& E1[DI+2] == 'M'
&& E1[DI+3] == 'S'
&& E1[DI+4] == 'P'
&& E1[DI+5] == 'E'
&& E1[DI+6] == 'C') break;
DI++;
}
DI = DI + 8; // move past "COMSPEC="
CopyString.Segment = ES; // Segment for source string for String Copy
COMSPECString = DI; // Copy the String in
CopyString.Segment = SS; // Default segment for strings
if(CommandLine[0]) COMSPECString = COMSPECString + " /C " + CommandLine;
//cout << COMSPECString << "\n";
AX = RunChildProgram(COMSPECString);
return AX;
}
int RunChildProgram(string CommandLine)
{
// Runs the DOS program specified by CommandLine
// This is a faster but more fussy version of Shell() for when you
// want to run another .EXE or .COM file
// Returns Error code (0 if No Error)
// The CommandLine must contain the name of the Executable file
// including the Extension. eg. "MIRROR.EXE"
// If the file is not in the current directory the full path must be given
// eg. "C:\TEST\MIRROR.EXE"
// Parameters can be put on the end. eg. "MIRROR.EXE /1"
// This function is documented in DOS technical manuals as the EXEC function
preserve DI,SI,DX,ES;
//cout << CommandLine << "\n";
// Copy first part of CommandLine into ScratchPad starting at 0x80
BX = ScratchPadAddress + 0x80;
SI = CommandLine;
while(S1[SI] && S1[SI] != ' ') S1[BX++] = S1[SI++];
S1[BX++] = 0;
// Copy remainder of CommandLine into ScratchPad
DI_CommandLineAddress = BX;
BX++; // Leave room for Char Count Byte
S1[BX++] = ' '; // A space is required at the start of the Tail for reliable operation
if(S1[SI]) SI++; // Skip past space in Command Line if there is one
CL_CharCount = 1; // Count the characters in the Tail. Already have 1
while(S1[SI]) { S1[BX++] = S1[SI++]; CL_CharCount++; } // Copy Tail in
S1[BX++] = 13; S1[BX++] = 0; // Terminate the Tail
S1[DI] = CL; // Store the Tail length
//PrintScratchPad();
// Set up the parameter block in the ScratchPad at address 0x100
BX = ScratchPadAddress + 0x100;
ES = Start.PSPAddress;
S2[BX] = E2[2Ch]; // Environment Address
//S2[BX] = 0; // Use default Environment Address
S2[BX+2] = DI_CommandLineAddress; S2[BX+4] = SS; // Command Tail (Command Line Parameters)
S2[BX+6] = ScratchPadAddress + 0x120; S2[BX+8] = SS; // Default FCB 1 Address. See below
S2[BX+10] = ScratchPadAddress + 0x120; S2[BX+12] = SS; // Default FCB 2 Address. See below
// Put Default FCB Data in ScratchPad at address 0x120
S1[BX+0x120] = 0;
for(SI=0x121; SI<0x12C; SI++) S1[BX+SI] = ' ';
for(SI=0x12C; SI<0x145; SI++) S1[BX+SI] = 0;
//PUSH ALL; cout << "Command = "; PrintString(1, ScratchPadAddress + 0x80); cout << "\n"; POP ALL;
//PUSH ALL; cout << "Tail = "; PrintString(1, DI + 1); cout << "\n"; POP ALL;
// Set up the pointers and call the child program
ES = SS; // ES:BX points to parameter block. BX already set
PUSH DS; // Preserve DS
DX = ScratchPadAddress + 0x80; DS = SS; // DS:DX points to Command Line
AX = 4B00h; INT 21h; // Call the Child Program
POP DS; // Restore DS
if(CARRYFLAG) { Error = AX; return AX; }
return 0;
}
void Beep()
{
// Makes a Beep
preserve DX;
#ifdef TSR
BX = ScratchPadAddress;
S1[BX] = 7; // Bell
BX = 1; CX = 1; DX = ScratchPadAddress;
PUSH DS; DS = SS; AH = 40h; INT 21h; POP DS; // Write character
#else
DL = 7; AH = 2; INT 21h; // print "BELL"
#endif
}
string GetTime()
{
// Returns a string containing the Time
string Time;
preserve DX;
AH = 2Ch; INT 21h; // Get Time
// CH = Hours
// CL = Minutes
// DH = Seconds
// DL = Hundredths of a Second
DL = CL;
Time = ByteToString(CH) + ":";
if(DL < 10) Time = Time + "0";
Time = Time + ByteToString(DL) + ":";
if(DH < 10) Time = Time + "0";
Time = Time + ByteToString(DH);
return Time;
}
string GetDate()
{
// Returns a string containing the Date
string Date;
preserve DX,DI;
DX = ScratchPadAddress;
PUSH DS; DS = SS; AX = 3800h; INT 21h; POP DS; // Get country info
BX = ScratchPadAddress; DI_DateFormat = S2[BX];
if(CARRYFLAG) DI_DateFormat = 0; // if no date information use US format
AH = 2Ah; INT 21h; // Get Date
// returns AL = Day of week , 0 = Sunday
// CX = Year
// DH = Month
// DL = Day
if(DI_DateFormat == 0)
{
DI = CX;
Date = ByteToString(DH) + "-" + ByteToString(DL) + "-" + IntegerToString(DI);
}
else if(DI_DateFormat == 1)
{
DI = CX;
Date = ByteToString(DL) + "-" + ByteToString(DH) + "-" + IntegerToString(DI);
}
else
{
DI = CX;
Date = IntegerToString(DI) + "-" + ByteToString(DH) + "-" + ByteToString(DL);
}
return Date;
}
void Delay(BX_Delay)
{
// Delays the program for the Delay specified in Hundredths of a second
preserve ESI, EDI;
ESI = 0; SI = BX; // Save delay time in 32 bit integer
AH = 2Ch; INT 21h; // Get current time
// Calculate initial time in hundreths of a second
EBX = 0; // Running total
EAX = 0; // Temporary Variable
BL = CH; // EBX = Total Hours
AL = CL; EBX = EBX * 60 + EAX; // EBX = Total Minutes
AL = DH; EBX = EBX * 60 + EAX; // EBX = Total Seconds
AL = DL; EBX = EBX * 100 + EAX; // EBX = Total Hundreths of a second
ESI_FinishTime = ESI + EBX; // Calculate finish time
while(EBX_CurrentTime < ESI_FinishTime) // while time not up
{
EDI_PreviousTime = EBX_CurrentTime; // Used to check for going past midnight
AH = 2Ch; INT 21h; // Get current time
// Calculate time in hundreths of a second
EBX = 0; // Running total
EAX = 0; // Temporary Variable
BL = CH; // EBX = Total Hours
AL = CL; EBX = EBX * 60 + EAX; // EBX = Total Minutes
AL = DH; EBX = EBX * 60 + EAX; // EBX = Total Seconds
AL = DL; EBX = EBX * 100 + EAX; // EBX = Total Hundreths of a second
if(EBX_CurrentTime < EDI_PreviousTime) EBX = EBX + 8640000; // Gone past midnight
}
}
string GetCurrentDirectory()
{
// Returns a string containing the Current Directory
string Directory;
preserve SI;
PUSH DS; DS = SS; SI = ScratchPadAddress;
DL = 0; // For Current Drive
AH = 47h; INT 21h;
AX = DS; POP DS;
Directory = "\\" + ScratchPadAddress; // Note that "\\" is really "\". See notes on string constants
return Directory;
}
string GetCurrentDrive()
{
// Returns a string containing the Current Drive
string Drive;
preserve SI;
Drive = "A:";
AH = 19h; INT 21h;
Drive[0] = Drive[0] + AL;
return Drive;
}
unsigned int CreateNewStructure(AX_StringFirstAddress, CX_StringLastAddress, BX_Size)
{
preserve DX, ES;
PUSH AX; PUSH CX;
DX_Segment = AllocateMemoryBlock(BX_Size);
POP CX; POP AX;
if(DX_Segment == 0) return DX_Segment; // Not enough memory
ES = DX_Segment;
E2[00h] = AX;
E2[02h] = CX;
if(CX_StringLastAddress >= AX_StringFirstAddress)
{
// if the structure has strings then store the Structure address for TidyStringHeap
CL_Found = 0;
for(BX=0; BX<MAXSTRUCTURES; BX++)
if(TidyStringHeap.StructureSegment[BX] == 0)
{ TidyStringHeap.StructureSegment[BX] = DX; CL_Found = 1; break; }
if(!CL_Found) End(202); // Too many String Structures
}
// Initialise all the string pointers
for(BX=E2[00]; BX<=E2[02]; BX++) E2[BX] = StringHeapStartAddress + 2; // NULL String Address
return DX_Segment;
}
void DeleteStructure(CX_Segment)
{
preserve ES, SI;
ES = CX_Segment;
if(E2[02h] >= E2[00h]) // if(StringLastAddress >= StringFirstAddress)
{
// If the structure has strings then remove the Structure address from TidyStringHeap
for(BX=0; BX<MAXSTRUCTURES; BX++)
if(TidyStringHeap.StructureSegment[BX] == CX)
{ TidyStringHeap.StructureSegment[BX] = 0; break; }
// Delete the strings from the String Heap
for(SI=E2[00]; SI<=E2[02]; SI++)
{
BX = E2[SI];
while(S1[BX] != 0) S1[BX++] = 0;
E2[SI] = "Undefined";
}
}
ReleaseMemoryBlock(CX);
}
void DisableControlC()
{
preserve DX;
AX = ADDRESSOF(IgnoreControlC); // Get address of Interrupt
PUSH DS;
DX = AX; DS = CS; // Set up address of interrupt for Control C
AH = 25h; AL = 23h; INT 21h; // Set new interrupt vector for INT 23h
POP DS;
}
interrupt IgnoreControlC()
{
// When Control C is disabled and a Control C is detected this Interrupt
// which does nothing is run every time a Control C is pressed
}
void EnableControlC()
{
preserve ES;
ES = Start.PSPAddress;
PUSH DS;
DX = E2[0Eh]; DS = E2[10h]; // Set up origional address of Control C Interrupt
AH = 25h; AL = 23h; INT 21h; // Set new interrupt vector for INT 23h
POP DS;
}